# -*- coding: utf-8 -*-
"""
Created on Mon Nov 18 23:24:35 2019

@author: XCL01
"""

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import random
import math

def init_center(dataSet,k):    #初始化聚类中心
    m = np.shape(datamat)[0]  
    dataIndex = list(range(m))
    random.shuffle(dataIndex)  
    centerIndex = dataIndex[0:k]
    initPoint = datamat.iloc[centerIndex]
    return np.mat(initPoint)    

def dist_eclud(vecA, vecB):    #算距离, 两个向量间欧式距离
    return math.sqrt(np.sum(np.power(vecA - vecB, 2)))

def kmeans(dataSet,k):  # 聚类算法的主体部分
    m=np.shape(dataSet)[0]  # 获得样本总数
    clusterAssment=np.mat(np.zeros((m,2)))   # 新建一个存储[族序号，距离的平方]

    center_point=init_center(dataSet,k)   # 随机产生初始的聚类中心
    print('最初的中心=', center_point)
    flag_cluster_changed =True   # 设置标志位看看前后的聚类中心有没有变化
    iteration_time=0  # 查看迭代次数
    while flag_cluster_changed :    # 所有样本分配结果不再改变，迭代终止
        flag_cluster_changed  = False
        #分配到最近的聚类中心对应的簇中
        for i in range(m):
            min_sq_dist  = math.inf;   #初始定义距离为无穷大
            min_sq_index  = -1        # 初始化索引值
            for j in range(k):  # 对于每一个实例样本来说，要遍历与所有中心的距离，然后，归为最近的哪一个簇中
                dist_J = dist_eclud(center_point[j, :], dataSet.values[i, :])  # 计算第i个样本到第j个中心点的距离
                if dist_J < min_sq_dist :   # 判断距离是否为最小
                    min_sq_dist  = dist_J   # 更新获取到最小距离
                    min_sq_index  = j     # 获取对应的簇序号
            if clusterAssment[i, 0] != min_sq_index :  # 样本上次分配结果跟本次不一样，标志位flag_cluster_changed 置True
                flag_cluster_changed  = True
            clusterAssment[i, :] = min_sq_index , min_sq_dist  ** 2  # 分配样本到最近的簇            
        iteration_time += 1
        sse = sum(clusterAssment[:, 1])
        print('第{}次迭代结果，畸变函数值为{}'.format(iteration_time, sse))
        #  更新聚类中心
        for center in range(k):  # 样本分配结束后，重新计算聚类中心
            ptsInClust = dataSet.iloc[np.nonzero(clusterAssment[:, 0].A == center)[0]]  # 获取该簇所有的样本点
            center_point[center, :] = np.mean(ptsInClust, axis=0) # 更新聚类中心：axis=0沿列方向求均值。
    print('最终的聚类中心为：{}'.format(center_point))
    return center_point, clusterAssment

def datashow(dataSet):    # 二维空间显示聚类结果
    num,dim = np.shape(dataSet)    
    for i in range(num):  # 绘所有样本
        mat_index=int(dataSet.iat[i,2])
        plt.plot(dataSet.iat[i,0],dataSet.iat[i,1],marksamples[mat_index],markersize=5) # 特征维对应坐标轴x,y；样本图形标记及大小
    plt.title('k-means cluster result')
    plt.xlabel('x lable')
    plt.ylabel('y lable')
    plt.show()
  
def trgartshow(dataSet):  # 画出实际图像
    num, dim = np.shape(dataSet) 
    center_point, clusterAssment = kmeans(dataSet,k)
    for i in range(num): # 绘所有样本
        markindex = int(clusterAssment[i, 0]) 
        plt.plot(dataSet.iat[i, 0], dataSet.iat[i, 1], marksamples[markindex], markersize=6)# 特征维对应坐标轴x,y；样本图形标记及大小
    for i in range(k):
        plt.plot(center_point[i, 0], center_point[i, 1], markcentroids[i], markersize=15, label=label[i], color=color[i])
        plt.legend(loc='upper left')
    plt.xlabel('x lable')
    plt.ylabel('y lable')
    plt.title('iris true result')  
    plt.show()
    
if __name__ == '__main__':
    k=5  #聚类数
    marksamples = ['ob', 'or', 'og', 'ok','^r', '^b' ,'^g', '^k']
    markcentroids = ['*', '^','<','p','o', '1','2','3']   
    label = ['1', '2', '3', '4', '5', '6', '7', '8']   
    color = ['red','orange','yellow', 'green', 'blue','black' , 'pink', 'red']#聚类中心颜色
    dataset = pd.read_csv("D:\Files\machinelearning_homework\homework_03_kmeans\dataset_circles.csv",header=None)
    datamat = dataset.loc[:,[0,1,2]]
    datashow(datamat)
    trgartshow(datamat)